[id].vue 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331
  1. // 商品详情页面
  2. <template>
  3. <div>
  4. <StoreHeaderCat ref="headercat" @updateFllow="updateFllow" />
  5. <div class="sld_goods_detail" v-loading="firstLoading">
  6. <!-- 内容区顶部固定 start -->
  7. <div class="contain_con" v-if="containCon">
  8. <div class="contain_content flex_row_center_center">
  9. <div class="contain_con_left flex_row_start_center">
  10. <span
  11. class="store_type"
  12. v-if="goodsDetail.data.storeInf.isOwnStore == 1"
  13. >{{ L["自营"] }}</span
  14. >
  15. <span class="store_title">{{
  16. goodsDetail.data.storeInf.storeName
  17. }}</span>
  18. </div>
  19. <div class="contain_con_right">
  20. <div class="goods_description_title flex_row_between_center">
  21. <div class="description_title_left flex_row_start_center">
  22. <span
  23. class="cursor_pointer"
  24. :class="{ description_active: goodsDesctionType == 'detail' }"
  25. @click="goodsDescType('detail')"
  26. >{{ L["商品详情"] }}</span
  27. >
  28. <span
  29. class="cursor_pointer"
  30. :class="{
  31. description_active: goodsDesctionType == 'evaluate',
  32. }"
  33. @click="goodsDescType('evaluate')"
  34. >{{ L["商品说明"] }}</span
  35. >
  36. <span
  37. class="cursor_pointer"
  38. :class="{
  39. description_active: goodsDesctionType == 'service',
  40. }"
  41. @click="goodsDescType('service')"
  42. >{{ L["商品服务"] }}</span
  43. >
  44. <span
  45. class="cursor_pointer"
  46. :class="{
  47. description_active: goodsDesctionType == 'salestore',
  48. }"
  49. @click="goodsDescType('salestore')"
  50. >{{ L["店铺热销"] }}</span
  51. >
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. </div>
  57. <!-- 内容区顶部固定 end -->
  58. <div class="goods_detail_content self_background" v-if="!firstLoading && goodsDetail.data.state === 3">
  59. <!-- 商品所属分类 ,联系客服,关注店铺 start-->
  60. <div class="goods_about_con">
  61. <div class="goods_about flex_row_between_center">
  62. <div class="goods_classify">
  63. <span
  64. v-for="(item, index) in goodsDetail.data.categoryPath"
  65. :key="index"
  66. >
  67. <nuxt-link
  68. :to="`/goods/list/${calcProductName(item)}_v-${ goodsDetail.data.categoryIdPath[index] }_gid-${index + 1}${index > 0? '_pid-' + goodsDetail.data.categoryIdPath[index - 1]: '_pid-0'
  69. }`"
  70. target="_blank"
  71. >
  72. {{ item }}</nuxt-link
  73. >
  74. <i> > </i>
  75. </span>
  76. <span style="font-weight: bold;font-size: 12px;color: #036EB8;">{{
  77. goodsDetail.data.goodsName
  78. }}</span>
  79. </div>
  80. <div class="goods_about_right flex_row_between_center">
  81. <router-link
  82. target="_blank"
  83. :to="'/store/'+ calcProductName(goodsDetail.data.storeInf.storeName) +'_'+ goodsDetail.data.storeInf.storeId"
  84. class="goods_about_store flex_row_between_center"
  85. v-if="goodsDetail.data.storeInf"
  86. >
  87. <span>{{ goodsDetail.data.storeInf.storeName }}</span>
  88. <span
  89. v-if="
  90. goodsDetail.data.storeInf &&
  91. goodsDetail.data.storeInf.isOwnStore == '1'
  92. "
  93. >{{ L["自营"] }}</span
  94. >
  95. </router-link>
  96. <div
  97. class="contact_service focus_store"
  98. @click="focusStore"
  99. v-if="goodsDetail.data.storeInf"
  100. >
  101. <span>
  102. {{
  103. goodsDetail.data.storeInf.isFollowStore
  104. ? L["取消关注"]
  105. : L["关注店铺"]
  106. }}
  107. </span>
  108. <img
  109. src="/goods/collection.png"
  110. alt=""
  111. v-show="goodsDetail.data.storeInf.isFollowStore == true"
  112. />
  113. <img
  114. src="/goods/no_collection.png"
  115. alt=""
  116. v-show="goodsDetail.data.storeInf.isFollowStore == false"
  117. />
  118. </div>
  119. </div>
  120. </div>
  121. </div>
  122. <!-- 商品所属分类 ,联系客服,关注店铺 end-->
  123. <!-- 商品主要信息 start -->
  124. <div class="main_con">
  125. <!-- 商品相关 start -->
  126. <div class="goods_des">
  127. <!-- 商品图片列表 start -->
  128. <div class="goods_des_left">
  129. <!-- 商品放大镜效果 start-->
  130. <div class="goods_main_picture">
  131. <div class="mask" v-if="goodsDetail.data.state && goodsDetail.data.state !== 3">
  132. <div class="circle">{{ L['已下架'] }}</div>
  133. </div>
  134. <div
  135. class="preview-box"
  136. @mousemove="move($event)"
  137. @mouseleave="out($event)"
  138. @mouseenter="enter($event)"
  139. ref="previewBox"
  140. >
  141. <div
  142. class="imageBorder"
  143. :class="{
  144. default_image: true,
  145. skeleton_default_image: firstLoading,
  146. }"
  147. :style="{ backgroundImage: 'url(' + defaultImage + ')' }"
  148. >
  149. <video
  150. v-if="
  151. currentDefaultImage == -1 && goodsDetail.data.goodsVideo
  152. "
  153. controls
  154. playsinline="playsinline"
  155. class="imageBorder default_image"
  156. :poster="defaultImage"
  157. autoplay
  158. ref="video"
  159. >
  160. <source
  161. :src="goodsDetail.data.goodsVideo"
  162. type="video/mp4"
  163. />
  164. </video>
  165. </div>
  166. <div
  167. class="v_btn"
  168. v-if="
  169. currentDefaultImage != -1 && goodsDetail.data.goodsVideo
  170. "
  171. >
  172. <img src="/goods/playV.png" alt="" @click="playV" />
  173. </div>
  174. <!-- 遮罩 start-->
  175. <div class="mask" ref="maskBox" v-show="maskShow"></div>
  176. <!-- 遮罩 end -->
  177. <!-- 底部放大镜icon图标 start -->
  178. <div
  179. class="magnifier_icon flex_row_center_center"
  180. v-show="!maskShow"
  181. >
  182. <i class="iconfont icon-sousuo"></i>
  183. </div>
  184. <!-- 底部放大镜icon图标 end -->
  185. </div>
  186. <!-- 右侧的放大后的图片 start -->
  187. <div
  188. class="goods_picture_big"
  189. style="border: 1px solid #eee"
  190. ref="zoomBox"
  191. v-show="maskShow"
  192. >
  193. <div
  194. class="default_image_big"
  195. :style="{ backgroundImage: 'url(' + defaultImage + ')' }"
  196. ref="pictureBig"
  197. ></div>
  198. </div>
  199. <!-- 右侧的放大后的图片 end -->
  200. </div>
  201. <!-- 商品放大镜效果 end -->
  202. <!-- 商品图片列表 start -->
  203. <div
  204. :class="{
  205. goods_picture_con: true,
  206. flex_row_between_center: true,
  207. skeleton_goods_picture_con: firstLoading,
  208. }"
  209. v-if="
  210. goodsDetail.data.defaultProduct &&
  211. goodsDetail.data.defaultProduct.goodsPics &&
  212. goodsDetail.data.defaultProduct.goodsPics.length > 0
  213. "
  214. >
  215. <i
  216. class="iconfont icon-ziyuan2 left_arrow"
  217. :class="{ no_left_arrow: currentDefaultImage == 0 }"
  218. @click="switchDefaultImage('left')"
  219. ></i>
  220. <div class="show_box">
  221. <ul
  222. class="goods_picture_list flex_row_start_center"
  223. ref="goodsPictureList"
  224. >
  225. <li
  226. v-for="(goodsImgItem, goodsImgIndex) in goodsDetail.data
  227. .defaultProduct.goodsPics"
  228. :key="goodsImgIndex"
  229. class="goods_img"
  230. :class="{
  231. goods_img_active: currentDefaultImage == goodsImgIndex,
  232. }"
  233. @click="selectDefaultImage(goodsImgItem, goodsImgIndex)"
  234. @mouseover="
  235. selectDefaultImage(goodsImgItem, goodsImgIndex)
  236. "
  237. >
  238. <div
  239. class="goods_image"
  240. :style="{
  241. backgroundImage: 'url(' + goodsImgItem + ')',
  242. }"
  243. ></div>
  244. </li>
  245. </ul>
  246. </div>
  247. <i
  248. class="iconfont icon-ziyuan11 right_arrow"
  249. :class="{
  250. no_left_arrow:
  251. currentDefaultImage ==
  252. goodsDetail.data.defaultProduct.goodsPics.length - 1,
  253. }"
  254. @click="switchDefaultImage('right')"
  255. ></i>
  256. </div>
  257. <!-- 商品图片列表 end -->
  258. <!-- 商品分享和收藏 start -->
  259. <!-- <div
  260. class="collection_share_btn flex_row_start_start"
  261. v-if="goodsDetail.data.state == 3"
  262. >
  263. <div
  264. class="collection_btn flex_row_start_center cursor_pointer"
  265. @click="collectGoods"
  266. >
  267. <img
  268. src="/goods/collection.png"
  269. alt=""
  270. v-if="goodsDetail.data.followGoods"
  271. />
  272. <img src="/goods/collection1.png" alt="" v-else />
  273. <span>{{
  274. goodsDetail.data.followGoods ? L["已收藏"] : L["收藏"]
  275. }}</span>
  276. </div>
  277. <div class="share_btn">
  278. <AddThis publicId="ra-5ab34ca22008ed41" />
  279. </div>
  280. </div> -->
  281. <!-- 商品分享和收藏 end -->
  282. </div>
  283. <!-- 商品图片列表 end -->
  284. <!-- 商品详细信息 start -->
  285. <div class="m_item_inner">
  286. <div class="item_info">
  287. <div
  288. :class="{
  289. detaile_name: true,
  290. skeleton_detaile_name: firstLoading,
  291. }"
  292. >
  293. {{ goodsDetail.data.goodsName }}
  294. </div>
  295. <div
  296. :class="{ p_ad: true, skeleton_p_ad: firstLoading }"
  297. v-if="goodsDetail.data.goodsBrief"
  298. >
  299. {{ goodsDetail.data.goodsBrief }}
  300. </div>
  301. <!-- 商品未下架即正常商品 start -->
  302. <div v-if="goodsDetail.data.state == 3 || firstLoading">
  303. <div class="summary">
  304. <div
  305. class="sld_summary_item summary_goods clearfix"
  306. v-if="goodsDetail.data.defaultProduct"
  307. >
  308. <div class="sld_summary_goods_left">
  309. <div class="goods_price flex_row_start_center">
  310. <!-- 在售价 -->
  311. <div>
  312. <span class="price_title">{{ L["价格"] }}</span>
  313. <strong
  314. :class="{
  315. p_price: true,
  316. skeleton_p_price: firstLoading,
  317. }"
  318. >
  319. <span>
  320. {{
  321. goodsDetail.data.goodsMoney == null
  322. ? "面议"
  323. : goodsDetail.data.goodsMoney
  324. }}
  325. </span>
  326. </strong>
  327. </div>
  328. </div>
  329. </div>
  330. </div>
  331. </div>
  332. <div v-if="
  333. goodsDetail.data.goodsMinOrder != undefined &&
  334. goodsDetail.data.goodsMinOrder
  335. " class="goodsMinOrder">
  336. <span class="">{{ L["最低采购数量:"] }}</span>
  337. <strong>
  338. <span>
  339. {{
  340. goodsDetail.data.goodsMinOrder == null
  341. ? "--"
  342. : goodsDetail.data.goodsMinOrder
  343. }}
  344. </span>
  345. </strong>
  346. </div>
  347. <!-- 商品简介 start -->
  348. <div class="summary-info">
  349. <div
  350. v-if="
  351. goodsDetail.data.goodsSummary != undefined &&
  352. goodsDetail.data.goodsSummary
  353. "
  354. class="summary_html"
  355. :style="goodsDetail.data.goodsSummaryBg"
  356. >
  357. <div
  358. class="summary_htmls"
  359. v-html="goodsDetail.data.goodsSummary"
  360. ></div>
  361. </div>
  362. </div>
  363. <!-- 规格 start -->
  364. <div
  365. class="goods_spec"
  366. v-if="
  367. goodsDetail.data.specs &&
  368. goodsDetail.data.specs.length > 0
  369. "
  370. >
  371. <div
  372. class="goods_spec_pre flex_row_start_start"
  373. v-for="(specItem, specIndex) in goodsDetail.data.specs"
  374. :key="specIndex"
  375. >
  376. <div
  377. class="goods_spec_pre_title"
  378. :title="specItem.specName"
  379. >
  380. {{ specItem.specName }}
  381. </div>
  382. <div class="goods_spec_pre_list flex_row_start_center">
  383. <!-- checkState : 1-选中,2-可选,3-禁用 -->
  384. <!-- 禁止选择 -->
  385. <div
  386. class="specval_pre cursor_pointer"
  387. :class="{
  388. specval_pre_disabled: sepcValItem.checkState == '3',
  389. }"
  390. v-for="(
  391. sepcValItem, specValIndex
  392. ) in specItem.specValueList"
  393. :key="specValIndex"
  394. v-show="sepcValItem.checkState == '3'"
  395. >
  396. <div
  397. class="specval_pre_image"
  398. :style="{
  399. backgroundImage: 'url(' + sepcValItem.image + ')',
  400. }"
  401. :title="sepcValItem.specValue"
  402. v-if="sepcValItem.image"
  403. ></div>
  404. <span class="specval_pre_text" v-else>{{
  405. sepcValItem.specValue
  406. }}</span>
  407. </div>
  408. <!-- 可选择 -->
  409. <div
  410. class="specval_pre cursor_pointer"
  411. :class="{
  412. specval_pre_active: sepcValItem.checkState == '1',
  413. }"
  414. v-for="(
  415. sepcValItem, specValIndex
  416. ) in specItem.specValueList"
  417. :key="specValIndex"
  418. @click="
  419. selectSpecVal(
  420. 'choice',
  421. specItem.specId,
  422. sepcValItem.specValueId
  423. )
  424. "
  425. v-show="sepcValItem.checkState != '3'"
  426. >
  427. <div
  428. class="goods_image"
  429. :style="{
  430. backgroundImage: 'url(' + goodsImgItem + ')',
  431. }"
  432. ></div>
  433. <div
  434. class="specval_pre_image"
  435. :style="{
  436. backgroundImage: 'url(' + sepcValItem.image + ')',
  437. }"
  438. :title="sepcValItem.specValue"
  439. v-if="sepcValItem.image"
  440. ></div>
  441. <span class="specval_pre_text" v-else>{{
  442. sepcValItem.specValue
  443. }}</span>
  444. <img
  445. src="/goods/check_mark.png"
  446. alt=""
  447. class="check_mark"
  448. v-if="sepcValItem.checkState == '1'"
  449. />
  450. </div>
  451. </div>
  452. </div>
  453. </div>
  454. <!-- 规格 end -->
  455. </div>
  456. <!-- 商品未下架即正常商品 end -->
  457. <template v-if="!firstLoading">
  458. <!-- 立即购买 加入购物车 收藏 ,分享 start-->
  459. <!-- 商品已下架 start -->
  460. <div
  461. class="options_btn"
  462. v-if="goodsDetail.data.state && goodsDetail.data.state != 3"
  463. >
  464. <p class="option_desc">{{L['商品已下架,欢迎挑选其他商品~']}}</p>
  465. <div class="goods_off_shelves">{{ L["商品已下架"] }}</div>
  466. <!-- 商品下架时的推荐商品 start -->
  467. <div class="recoOffShop">
  468. <div
  469. class="reCon"
  470. v-for="(
  471. {
  472. goodsImage,
  473. goodsName,
  474. goodsPrice,
  475. defaultProductId,
  476. },
  477. index
  478. ) in recomOffShop.data"
  479. :key="index"
  480. >
  481. <div class="reComImg">
  482. <router-link
  483. :to="'/goods/detail/'+ calcProductName(goodsName) +'_'+ defaultProductId"
  484. target="_blank"
  485. >
  486. <img :src="goodsImage" alt="" />
  487. </router-link>
  488. </div>
  489. <router-link
  490. class="recomName"
  491. :to="'/goods/detail/'+ calcProductName(goodsName) +'_'+ defaultProductId"
  492. target="_blank"
  493. >
  494. {{ goodsName }}
  495. </router-link>
  496. <span class="recomPrice">{{ goodsMoney }}</span>
  497. </div>
  498. </div>
  499. <!-- 商品下架时的推荐商品 end -->
  500. </div>
  501. <!-- 商品已下架 end -->
  502. <!-- 普通(活动)正常商品 start -->
  503. <div class="options_btn flex_row_start_center" v-else>
  504. <div
  505. class="goods_code"
  506. id="qrcodeAct"
  507. v-show="isShowQr"
  508. ></div>
  509. <div v-if="goodsDetail.data.state === 3" class="buy_now flex_row_center_center mt-20" @click="goBuy">
  510. {{ L["发送询盘"] }}
  511. </div>
  512. <div
  513. class="collection_share_btn flex_row_start_start mt-20"
  514. v-if="goodsDetail.data.state == 3"
  515. >
  516. <div
  517. class="collection_btn flex_row_start_center cursor_pointer"
  518. @click="collectGoods"
  519. >
  520. <img
  521. src="/goods/collection.png"
  522. alt=""
  523. v-if="goodsDetail.data.followGoods"
  524. />
  525. <img src="/goods/collection1.png" alt="" v-else />
  526. <span>{{
  527. goodsDetail.data.followGoods ? L["已收藏"] : L["收藏"]
  528. }}</span>
  529. </div>
  530. <!-- <div class="share_btn">
  531. <AddThis publicId="ra-5ab34ca22008ed41" />
  532. </div> -->
  533. </div>
  534. </div>
  535. <!-- 普通(活动)正常商品 end -->
  536. <!-- 立即购买 加入购物车 收藏 ,分享 end-->
  537. </template>
  538. </div>
  539. </div>
  540. <!-- 商品详细信息 end -->
  541. <!-- 相关推荐 start -->
  542. <div class="more_goods" v-if="goodsDetail.data.state == 3">
  543. <div class="more_goods_title">{{ L["看了又看"] }}</div>
  544. <div class="more_goods_list flex_column_center_center">
  545. <template v-if="firstLoading">
  546. <div
  547. class="more_goods_pre"
  548. v-for="(recommendItem, recommendIndex) in [
  549. { a: 1 },
  550. { b: 2 },
  551. { c: 3 },
  552. ]"
  553. :key="recommendIndex"
  554. >
  555. <router-link
  556. target="_blank"
  557. :to="'/goods/detail/'+ calcProductName(recommendItem.goodsName) +'_'+ recommendItem.defaultProductId"
  558. >
  559. <div
  560. :class="{
  561. more_goods_pre_img: true,
  562. skeleton_more_goods_pre_img: firstLoading,
  563. }"
  564. ></div>
  565. <p
  566. :class="{
  567. skeleton_more_goods_pre_goods_name: firstLoading,
  568. }"
  569. >
  570. {{ recommendItem.goodsName }}
  571. </p>
  572. <p>
  573. <span
  574. :class="{
  575. skeleton_more_goods_pre_goods_price: firstLoading,
  576. }"
  577. ></span>
  578. </p>
  579. </router-link>
  580. </div>
  581. </template>
  582. <template v-else>
  583. <div
  584. class="more_goods_pre"
  585. v-for="(
  586. recommendItem, recommendIndex
  587. ) in recommendeList.data"
  588. :key="recommendIndex"
  589. v-show="recommendIndex < 2"
  590. >
  591. <router-link
  592. target="_blank"
  593. :to="'/goods/detail/'+ calcProductName(recommendItem.goodsName) +'_'+ recommendItem.defaultProductId"
  594. >
  595. <div class="more_goods_pre_img flex_row_center_center">
  596. <img
  597. :src="recommendItem.goodsImage"
  598. :title="recommendItem.goodsName"
  599. />
  600. </div>
  601. <p>{{ recommendItem.goodsName }}</p>
  602. <p>{{ recommendItem.goodsMoney }}</p>
  603. </router-link>
  604. </div>
  605. </template>
  606. </div>
  607. </div>
  608. <!-- 相关推荐 end -->
  609. </div>
  610. <!-- 商品相关 end -->
  611. <!-- 店铺,及各种信息的切换 start -->
  612. <div
  613. class="container flex_row_start_start"
  614. ref="container"
  615. id="container"
  616. >
  617. <div class="left">
  618. <div class="store_info" v-if="goodsDetail.data.storeInf">
  619. <div class="store_info_title flex_row_start_center">
  620. <span
  621. class="store_type"
  622. v-if="goodsDetail.data.storeInf.isOwnStore == 1"
  623. >{{ L["自营"] }}</span
  624. >
  625. <router-link
  626. target="_blank"
  627. :to="'/store/'+ calcProductName(goodsDetail.data.storeInf.storeName) +'_'+ goodsDetail.data.storeInf.storeId"
  628. >
  629. <span class="store_title" @click="goStore()">{{
  630. goodsDetail.data.storeInf.storeName
  631. }}</span>
  632. </router-link>
  633. </div>
  634. <div class="store_des">
  635. <div class="store_des_pre pre_service">
  636. <span>{{ L["客服电话"] }}:</span>
  637. <span>{{ goodsDetail.data.storeInf.servicePhone }}</span>
  638. </div>
  639. <div class="store_des_pre pre_service">
  640. <span>{{ L["主营商品"] }}:</span>
  641. <span>{{ goodsDetail.data.storeInf.mainBusiness }}</span>
  642. </div>
  643. </div>
  644. <div class="store_btn flex_row_center_center">
  645. <div class="store_btn_pre go_store_btn flex_row_center_center">
  646. <!-- <img src="/goods/store.png" alt="" class="btn_pre_img" /> -->
  647. <router-link
  648. target="_blank"
  649. :to="'/store/'+ calcProductName(goodsDetail.data.storeInf.storeName) +'_'+ goodsDetail.data.storeInf.storeId"
  650. >
  651. {{ L["进入店铺"] }}
  652. </router-link>
  653. </div>
  654. <div
  655. class="store_btn_pre follow_btn flex_row_center_center"
  656. @click="focusStore"
  657. >
  658. <!-- <img
  659. src="/goods/collection.png"
  660. alt=""
  661. v-if="goodsDetail.data.storeInf.isFollowStore"
  662. />
  663. <img src="/goods/no_collection.png" alt="" v-else /> -->
  664. <span>{{
  665. goodsDetail.data.storeInf.isFollowStore
  666. ? L["取消关注"]
  667. : L["关注店铺"]
  668. }}</span>
  669. </div>
  670. </div>
  671. </div>
  672. <!-- 店铺推荐及热门收藏 end -->
  673. </div>
  674. <!-- 商品详情 评价 商品服务 店铺热销 start-->
  675. <div class="goods_description">
  676. <div class="goods_description_title flex_row_between_center">
  677. <div class="description_title_left flex_row_start_center">
  678. <span
  679. class="cursor_pointer"
  680. :class="{
  681. description_active: goodsDesctionType == 'detail',
  682. }"
  683. @click="goodsDescType('detail')"
  684. >{{ L["商品详情"] }}</span
  685. >
  686. <span
  687. class="cursor_pointer"
  688. v-if="
  689. goodsDetail.data.goodsAnnexList &&
  690. goodsDetail.data.goodsAnnexList.length > 0
  691. "
  692. :class="{
  693. description_active: goodsDesctionType == 'evaluate',
  694. }"
  695. @click="goodsDescType('evaluate')"
  696. >{{ L["商品说明"] }}</span
  697. >
  698. <span
  699. class="cursor_pointer"
  700. v-if="
  701. goodsDetail.data.serviceLabels &&
  702. goodsDetail.data.serviceLabels.length > 0
  703. "
  704. :class="{
  705. description_active: goodsDesctionType == 'service',
  706. }"
  707. @click="goodsDescType('service')"
  708. >{{ L["商品服务"] }}</span
  709. >
  710. <span
  711. class="cursor_pointer"
  712. :class="{
  713. description_active: goodsDesctionType == 'salestore',
  714. }"
  715. @click="goodsDescType('salestore')"
  716. >{{ L["店铺推荐"] }}</span
  717. >
  718. </div>
  719. </div>
  720. <div class="goods_description_con">
  721. <!-- 商品详情,规格参数,及详情富文本 start-->
  722. <div class="goods_des_con" v-if="goodsDesctionType == 'detail'">
  723. <div
  724. v-if="
  725. goodsDetail.data.brandName ||
  726. (goodsDetail.data.goodsParameterList &&
  727. goodsDetail.data.goodsParameterList.length > 0) ||
  728. goodsDetail.data.goodsDetails
  729. "
  730. >
  731. <div class="brand" v-if="goodsDetail.data.brandName">
  732. <span>{{ L["品牌"] }}: </span>
  733. <span>{{ goodsDetail.data.brandName }}</span>
  734. </div>
  735. <div
  736. v-if="
  737. goodsDetail.data.goodsParameterList &&
  738. goodsDetail.data.goodsParameterList.length > 0
  739. "
  740. >
  741. <div
  742. class="goods_parameter_list"
  743. :class="{ goods_paramter_more: !parameterShow }"
  744. >
  745. <div
  746. class="goods_parameter_pre"
  747. v-for="(parameterItem, paramterIndex) in goodsDetail
  748. .data.goodsParameterList"
  749. :key="paramterIndex"
  750. >
  751. <span>{{ parameterItem.parameterName }}: </span>
  752. <span>{{ parameterItem.parameterValue }}</span>
  753. </div>
  754. </div>
  755. <div
  756. class="collapse_unfold flex_row_center_center cursor_pointer"
  757. v-if="goodsDetail.data.goodsParameterList.length > 16"
  758. @click="openParameter"
  759. >
  760. <span>{{
  761. !parameterShow ? L["查看全部"] : L["收起全部"]
  762. }}</span>
  763. <i
  764. class="iconfont icon-ziyuan11-copy"
  765. v-if="!parameterShow"
  766. ></i>
  767. <i class="iconfont icon-ziyuan11-copy-copy" v-else></i>
  768. </div>
  769. </div>
  770. <div
  771. v-if="
  772. goodsDetail.data.topTemplateContent != undefined &&
  773. goodsDetail.data.topTemplateContent
  774. "
  775. class="goods_html"
  776. :style="goodsDetail.data.topTemplateContentBg"
  777. >
  778. <div
  779. class="goods_htmls"
  780. v-html="goodsDetail.data.topTemplateContent"
  781. ></div>
  782. </div>
  783. <div
  784. v-if="
  785. goodsDetail.data.goodsDetails != undefined &&
  786. goodsDetail.data.goodsDetails
  787. "
  788. class="goods_html"
  789. :style="goodsDetail.data.goodsDetailsBg"
  790. >
  791. <div
  792. class="goods_htmls"
  793. v-html="goodsDetail.data.goodsDetails"
  794. ></div>
  795. </div>
  796. <div
  797. v-if="
  798. goodsDetail.data.bottomTemplateContent != undefined &&
  799. goodsDetail.data.bottomTemplateContent
  800. "
  801. class="goods_html"
  802. :style="goodsDetail.data.bottomTemplateContentBg"
  803. >
  804. <div
  805. class="goods_htmls"
  806. v-html="goodsDetail.data.bottomTemplateContent"
  807. ></div>
  808. </div>
  809. </div>
  810. <div v-else>
  811. <SldCommonEmpty
  812. :tip="L['该商品暂无详情~']"
  813. totalWidth="934px"
  814. />
  815. </div>
  816. </div>
  817. <!-- 商品详情,规格参数,及详情富文本 end-->
  818. <!-- 商品评价 start -->
  819. <div
  820. class="goods_comments"
  821. v-if="goodsDesctionType == 'evaluate'"
  822. >
  823. <!--说明书下载begin-->
  824. <div
  825. class="download_warp"
  826. v-if="
  827. goodsDetail.data.goodsAnnexList &&
  828. goodsDetail.data.goodsAnnexList.length > 0
  829. "
  830. >
  831. <span style="margin: 10px; font-size: 14px"
  832. >{{ L["说明书下载"] }}:
  833. </span>
  834. <br />
  835. <p
  836. style="margin: 10px; font-size: 14px"
  837. v-for="(item, index) in goodsDetail.data.goodsAnnexList"
  838. :key="index"
  839. >
  840. <a
  841. style="text-decoration: underline"
  842. href="javascript:;"
  843. @click="downloadAdd(item)"
  844. >{{ item.annexName }}</a
  845. >
  846. </p>
  847. </div>
  848. <div v-else>
  849. {{ L["暂无说明"] }}
  850. </div>
  851. <!--说明书下载end-->
  852. </div>
  853. <!-- 商品评价 end -->
  854. <!-- 商品服务 start -->
  855. <div
  856. class="goods_server_list"
  857. v-if="goodsDesctionType == 'service'"
  858. >
  859. <div
  860. v-if="
  861. goodsDetail.data.serviceLabels &&
  862. goodsDetail.data.serviceLabels.length > 0
  863. "
  864. >
  865. <div
  866. class="goods_server_pre"
  867. v-for="(serverItem, serverIndex) in goodsDetail.data
  868. .serviceLabels"
  869. :key="serverIndex"
  870. >
  871. <div class="server_pre_top flex_row_start_center">
  872. <span class="server_pre_tips"></span>
  873. <span class="server_pre_name">{{
  874. serverItem.labelName
  875. }}</span>
  876. </div>
  877. <div class="server_pre_content">
  878. {{ serverItem.description }}
  879. </div>
  880. </div>
  881. </div>
  882. <div v-else>
  883. <SldCommonEmpty
  884. :tip="L['暂无商品服务~']"
  885. totalWidth="934px"
  886. />
  887. </div>
  888. </div>
  889. <!-- 商品服务 end -->
  890. <!-- 店铺推荐 start -->
  891. <div
  892. class="store_hot_sales"
  893. v-if="goodsDesctionType == 'salestore'"
  894. >
  895. <div
  896. v-if="
  897. recommendedList.data && recommendedList.data.length > 0
  898. "
  899. >
  900. <div class="store_hot_sales_list">
  901. <div
  902. class="goods_pre flex_column_between_start"
  903. v-for="(
  904. recommendItem, recommendIndex
  905. ) in recommendedList.data"
  906. :key="recommendIndex"
  907. >
  908. <router-link
  909. target="_blank"
  910. :to="'/goods/detail/'+ calcProductName(recommendItem.goodsName) +'_'+ recommendItem.defaultProductId"
  911. >
  912. <div class="flex_column_start_start">
  913. <div
  914. class="goods_pre_img"
  915. :style="{
  916. backgroundImage:
  917. 'url(' + recommendItem.goodsImage + ')',
  918. }"
  919. ></div>
  920. <div class="goods_name">
  921. {{ recommendItem.goodsName }}
  922. </div>
  923. </div>
  924. <div class="goods_price">
  925. <div class="selling_price">
  926. {{ recommendItem.goodsMoney }}
  927. </div>
  928. </div>
  929. </router-link>
  930. </div>
  931. </div>
  932. <div
  933. class="flex_row_end_center sld_pagination sld_page_bottom"
  934. v-if="
  935. recommendeData.data.pagination &&
  936. recommendeData.data.pagination.total
  937. "
  938. >
  939. <el-pagination
  940. @current-change="handleCurrentChangeSales"
  941. v-model:currentPage="salesCurrent"
  942. :page-size="salesPageSize"
  943. layout="prev, pager, next, jumper"
  944. :total="recommendeData.data.pagination.total"
  945. :hide-on-single-page="true"
  946. >
  947. </el-pagination>
  948. </div>
  949. </div>
  950. <div
  951. v-if="
  952. recommendedList.data && recommendedList.data.length == 0
  953. "
  954. class="flex_column_center_center empty_data"
  955. >
  956. <SldCommonEmpty
  957. :tip="L['暂无相关商品~']"
  958. totalWidth="934px"
  959. />
  960. </div>
  961. </div>
  962. <!-- 店铺推荐 end -->
  963. </div>
  964. </div>
  965. <!-- 商品详情 评价 商品服务 店铺热销 end-->
  966. </div>
  967. <!-- 店铺,及各种信息的切换 end -->
  968. </div>
  969. <!-- 商品主要信息 end -->
  970. </div>
  971. <EnquiryModal
  972. v-if="enquiryVis"
  973. :itemType="'GOODS'"
  974. :itemId="productId"
  975. @closeLoingModal="closeEnquiryModal"
  976. />
  977. <div class="error-wrap" v-if="!firstLoading && goodsDetail.data.state !== 3">
  978. <div class="error-wrap-content">
  979. <div class="error-wrap-content__tip">
  980. <img src="/new_empty.png"/>
  981. <span class="text_1">{{ L['该产品已被下架或移除。'] }}</span>
  982. <span class="text_2">{{ L['谢谢您的关注!'] }}</span>
  983. </div>
  984. <div class="error-wrap-content__like">
  985. <div class="like_title">
  986. {{ L['看了又看'] }}
  987. </div>
  988. <div class="like_list">
  989. <div class="like_list_item" v-for="(recommendItem, recommendIndex) in recommendeList.data" :key="recommendIndex">
  990. <router-link
  991. target="_blank"
  992. :to="'/goods/detail/'+ calcProductName(recommendItem.goodsName) +'_'+ recommendItem.defaultProductId"
  993. >
  994. <div class="item-img">
  995. <img
  996. :src="recommendItem.goodsImage"
  997. :title="recommendItem.goodsName"
  998. />
  999. </div>
  1000. <div class="item-name">{{ recommendItem.goodsName }}</div>
  1001. </router-link>
  1002. </div>
  1003. </div>
  1004. </div>
  1005. </div>
  1006. </div>
  1007. </div>
  1008. </div>
  1009. </template>
  1010. <script setup>
  1011. import addrData from "@/assets/area.json";
  1012. import { ElMessage, ElRate, ElDialog, ElPagination } from "element-plus";
  1013. import { qrcanvas } from "qrcanvas";
  1014. // import { lang_zn } from "@/assets/language/zh";
  1015. import { getCurLanguage } from '@/composables/common.js';
  1016. import { goodsInfo, useUserInfo } from "@/store/user.js";
  1017. import { useFiltersStore } from "@/store/filter.js";
  1018. const filtersStore = useFiltersStore();
  1019. const goodsInfox = goodsInfo();
  1020. const configInfo = useUserInfo();
  1021. // const L = lang_zn;
  1022. const L = getCurLanguage();
  1023. const firstLoading = ref(true); //是否第一次加载
  1024. const router = useRouter();
  1025. const route = useRoute();
  1026. const store = ref();
  1027. const imgVisible = ref(false);
  1028. const imgSource = ref("");
  1029. const imgIndex = ref(-1);
  1030. const vid = ref(0); //店铺id
  1031. const proxy = getCurrentInstance();
  1032. const goodsDetail = reactive({ data: {} }); //商品详情数据
  1033. const isChoice = ref("default"); //是默认选中的,还是点击选择规格之后的 default:默认 choice:选择
  1034. const productId = ref(""); //货品id
  1035. const fullDisList = reactive({ data: [] }); //满优惠促销列表
  1036. const couponList = reactive({ data: [] }); //获取店铺优惠券列表
  1037. const pictureBig = ref(null); //大图的信息
  1038. const maskBox = ref(null); //遮罩盒子的信息
  1039. const maskShow = ref(false); //遮罩是否显示
  1040. const previewBox = ref(null); // 左侧主图元素信息
  1041. const zoomBox = ref(null); //左侧主图的父元素的信息
  1042. const defaultImage = ref(""); //默认主图路径
  1043. const currentDefaultImage = ref("0"); //默认主图显示第一张
  1044. const currentSpecNum = ref(1); //商品编辑数量,默认数量为1
  1045. const recommendeList = reactive({ data: [] }); //看了又看商品
  1046. const storePopularList = reactive({ data: [] }); //店铺推荐及热门收藏
  1047. const storePopularType = ref("collection"); //店铺推荐及热门收藏,默认显示店铺推荐
  1048. const goodsDesctionType = ref("detail"); //商品详情,评价,商品服务,店铺热销,默认显示商品详情
  1049. const goodsCommentsInfo = reactive({ data: {} }); //商品评价信息
  1050. const evaluationType = ref(""); //商品评价类型,默认显示全部
  1051. const recommendedList = reactive({ data: [] }); //店铺推荐列表
  1052. const evaluationCurrent = ref(1); //评价列表默认第一页
  1053. const evaluationPageSize = ref(5); //评价列表默认一页显示5条数据
  1054. const salesCurrent = ref(1); //店铺热销列表默认一页
  1055. const salesPageSize = ref(20); //店铺热销列表默认一页显示20条数据
  1056. const recommendeData = reactive({ data: {} }); //店铺热销推荐数据
  1057. const couponModel = ref(false); //优惠券弹框是否显示
  1058. const cartListData = reactive({ data: goodsInfox.cartListData }); //获取vux的store中的购物车数据
  1059. const enquiryVis = ref(false); //登录弹框是否显示,默认不显示
  1060. const container = ref(null); // 商品详情页底部内容区
  1061. const containerTop = ref(0); //商品详情页底部内容区的top值
  1062. const containCon = ref(false); //固定内容区头部
  1063. const fullDiscountModel = ref(false); //满优惠弹框是否显示
  1064. const goodsPictureList = ref(null); //商品图片列表
  1065. const curCouponPage = ref(1); //当前为第一页优惠券
  1066. const couponPageSize = ref(6); //优惠券默认一页显示6条数据
  1067. const wxShareCode = ref(false); //微信分享二维码是否显示
  1068. const parameterShow = ref(false); //规格参数查看是否查看全部,默认为否
  1069. const score = ref(0); //好评率
  1070. const colors = ref(["#E2231A", "#E2231A", "#E2231A"]); //星星颜色
  1071. const curAddr = ref(-1);
  1072. const curAddrName = ref("");
  1073. const addrIdx = ref(0);
  1074. const otherAddrIdx = ref(0);
  1075. const othTopIdx = reactive({
  1076. 0: 0,
  1077. 1: 0,
  1078. 2: 0,
  1079. });
  1080. const otherTree = ref([
  1081. addrData[othTopIdx["0"]],
  1082. addrData[othTopIdx["0"]].children[othTopIdx["1"]],
  1083. ]);
  1084. const othAddrDe = ref(addrData);
  1085. const addrDialogVisible = ref(false);
  1086. const logFlag = ref(configInfo.loginFlag);
  1087. // 促销活动信息
  1088. const preSellInfo = reactive({ data: {} });
  1089. const pinInfo = reactive({ data: {} });
  1090. const seckillInfo = reactive({ data: {} });
  1091. const ladderInfo = reactive({ data: {} });
  1092. const address_list = reactive({ data: [] });
  1093. const isShowQr = ref(false);
  1094. const secInt = ref("");
  1095. const time = reactive({
  1096. day: "00",
  1097. hours: "00",
  1098. minutes: "00",
  1099. seconds: "00",
  1100. });
  1101. const judgeStock = computed(() => {
  1102. return (
  1103. goodsDetail.data.defaultProduct.productStock == 0 ||
  1104. (JSON.stringify(preSellInfo.data) != "{}" &&
  1105. preSellInfo.data.presellStock == 0) ||
  1106. (JSON.stringify(pinInfo.data) != "{}" && pinInfo.data.spellStock == 0) ||
  1107. (JSON.stringify(seckillInfo.data) != "{}" &&
  1108. seckillInfo.data.seckillStock == 0)
  1109. );
  1110. });
  1111. // 促销活动信息end
  1112. const scrollHandle = async (e) => {
  1113. if (process.client) {
  1114. let elementScrollTop = e.srcElement.scrollingElement.scrollTop; //获取页面滚动高度
  1115. if (
  1116. document.getElementById("container") &&
  1117. elementScrollTop > document.getElementById("container").offsetTop
  1118. ) {
  1119. containCon.value = true;
  1120. await proxy.$nextTick();
  1121. } else {
  1122. containCon.value = false;
  1123. }
  1124. }
  1125. };
  1126. // 点击播放视频
  1127. const playV = () => {
  1128. currentDefaultImage.value = -1;
  1129. defaultImage.value = "";
  1130. maskShow.value = false;
  1131. videoEnd();
  1132. };
  1133. //获取商品详情数据
  1134. const getInitDataStatic = async (proId) => {
  1135. let params = {
  1136. productId: proId,
  1137. };
  1138. const { data: value, pending: pending } = await useFetchRaw(
  1139. apiUrl + "v3/goods/front/goods/details",
  1140. { params: params }
  1141. );
  1142. const res = value._rawValue;
  1143. if (res.state == 200) {
  1144. let staticData = [
  1145. "brandId",
  1146. "brandName",
  1147. "categoryPath",
  1148. "categoryIdPath",
  1149. "goodsBrief",
  1150. "goodsDetails",
  1151. "goodsSummary",
  1152. "goodsBrief",
  1153. "goodsId",
  1154. "goodsName",
  1155. "goodsParameterList",
  1156. "goodsVideo",
  1157. "topTemplateContent",
  1158. "bottomTemplateContent",
  1159. "goodsAnnexList",
  1160. "serviceLabels",
  1161. "goodsMoney",
  1162. "goodsMinOrder",
  1163. "goodsParameterList",
  1164. ];
  1165. staticData.forEach((item) => {
  1166. if (item == "categoryPath") {
  1167. goodsDetail.data.categoryPath = res.data.categoryPath.split("->");
  1168. } else if (item == "categoryIdPath") {
  1169. goodsDetail.data.categoryIdPath = [
  1170. res.data.categoryId1,
  1171. res.data.categoryId2,
  1172. res.data.categoryId3,
  1173. ];
  1174. } else {
  1175. goodsDetail.data[item] = res.data[item];
  1176. }
  1177. });
  1178. if (goodsDetail.data.goodsSummary) {
  1179. goodsDetail.data.goodsSummary = quillEscapeToHtml(
  1180. goodsDetail.data.goodsSummary
  1181. );
  1182. //处理背景样式
  1183. if (
  1184. goodsDetail.data.goodsSummary.indexOf(
  1185. '<p style="display:none;" data-background="'
  1186. ) != -1
  1187. ) {
  1188. let bg = goodsDetail.data.goodsSummary
  1189. .split('<p style="display:none;" data-background="')[1]
  1190. .split('">')[0];
  1191. goodsDetail.data.goodsSummaryBg = bg;
  1192. }
  1193. }
  1194. if (goodsDetail.data.topTemplateContent) {
  1195. goodsDetail.data.topTemplateContent = quillEscapeToHtml(
  1196. goodsDetail.data.topTemplateContent
  1197. );
  1198. //处理背景样式
  1199. if (
  1200. goodsDetail.data.topTemplateContent.indexOf(
  1201. '<p style="display:none;" data-background="'
  1202. ) != -1
  1203. ) {
  1204. let bg = goodsDetail.data.topTemplateContent
  1205. .split('<p style="display:none;" data-background="')[1]
  1206. .split('">')[0];
  1207. goodsDetail.data.topTemplateContentBg = bg;
  1208. }
  1209. }
  1210. if (goodsDetail.data.goodsDetails) {
  1211. goodsDetail.data.goodsDetails = quillEscapeToHtml(
  1212. goodsDetail.data.goodsDetails
  1213. );
  1214. //处理背景样式
  1215. if (
  1216. goodsDetail.data.goodsDetails.indexOf(
  1217. '<p style="display:none;" data-background="'
  1218. ) != -1
  1219. ) {
  1220. let bg = goodsDetail.data.goodsDetails
  1221. .split('<p style="display:none;" data-background="')[1]
  1222. .split('">')[0];
  1223. goodsDetail.data.goodsDetailsBg = bg;
  1224. }
  1225. }
  1226. if (goodsDetail.data.bottomTemplateContent) {
  1227. goodsDetail.data.bottomTemplateContent = quillEscapeToHtml(
  1228. goodsDetail.data.bottomTemplateContent
  1229. );
  1230. //处理背景样式
  1231. if (
  1232. goodsDetail.data.bottomTemplateContent.indexOf(
  1233. '<p style="display:none;" data-background="'
  1234. ) != -1
  1235. ) {
  1236. let bg = goodsDetail.data.bottomTemplateContent
  1237. .split('<p style="display:none;" data-background="')[1]
  1238. .split('">')[0];
  1239. goodsDetail.data.bottomTemplateContentBg = bg;
  1240. }
  1241. }
  1242. currentDefaultImage.value = 0;
  1243. vid.value = res.data.storeInf.storeId;
  1244. // setTimeout(() => {
  1245. // sldStatEvent({ behaviorType: 'gpv', goodsId: goodsDetail.data.goodsId, storeId: vid.value });
  1246. // }, 3000)
  1247. } else {
  1248. ElMessage.error(res.msg);
  1249. }
  1250. if (!pending._rawValue) {
  1251. getInitDataDynamic(productId.value);
  1252. getRecommend();
  1253. getStorePopular();
  1254. getEvaluation();
  1255. addLog();
  1256. }
  1257. };
  1258. getInitDataStatic(calcProductId(route.path));
  1259. onMounted(() => {
  1260. setTimeout(() => {
  1261. sldStatEvent({ behaviorType: 'gpv', goodsId: goodsDetail.data.goodsId, storeId: vid.value,pageUrl: defaultUrl + router.currentRoute.value.path, referrerPageUrl: apiUrl });
  1262. }, 3000)
  1263. });
  1264. productId.value = calcProductId(route.path);
  1265. if (!logFlag.value) {
  1266. addrIdx.value = 1;
  1267. }
  1268. const getInitDataDynamic = async (proId, updateType) => {
  1269. let params = {
  1270. productId: proId,
  1271. };
  1272. const { data: value, pending: pending } = await useFetchRaw(
  1273. apiUrl + "v3/goods/front/goods/details2",
  1274. { params: params,
  1275. headers:{Authorization:'Bearer ' + filtersStore.getToken}
  1276. }
  1277. );
  1278. const res = value._rawValue;
  1279. if (res.state == 200) {
  1280. useHead({
  1281. title: res.data.seoInfo.seoTitle || 'Goods Detail',
  1282. meta: [
  1283. {
  1284. name: "description",
  1285. content: res.data.seoInfo.seoDesc,
  1286. },
  1287. {
  1288. name: "keywords",
  1289. content: res.data.seoInfo.seoKeywords,
  1290. },
  1291. ],
  1292. });
  1293. defaultImage.value = res.data.defaultProduct.goodsPics[0];
  1294. let dynamicData = [
  1295. "defaultProduct",
  1296. "deliverInfo",
  1297. "effectSpecValueIds",
  1298. "followGoods",
  1299. "specs",
  1300. "storeInf",
  1301. "sales",
  1302. "state",
  1303. "shareLink",
  1304. "shareImage",
  1305. "goodsMinOrder",
  1306. "isVirtualGoods",
  1307. ];
  1308. dynamicData.forEach((item) => {
  1309. goodsDetail.data[item] = res.data[item];
  1310. });
  1311. if (goodsDetail.data.state != 3) {
  1312. getRecom();
  1313. }
  1314. if (goodsDetail.data.defaultProduct.promotionType == 103) {
  1315. getPreSell(res.data.defaultProduct.productId);
  1316. } else if (goodsDetail.data.defaultProduct.promotionType == 102) {
  1317. getPin();
  1318. } else if (goodsDetail.data.defaultProduct.promotionType == 104) {
  1319. getSeckill();
  1320. } else if (goodsDetail.data.defaultProduct.promotionType == 105) {
  1321. getLadder();
  1322. } else {
  1323. preSellInfo.data = {};
  1324. pinInfo.data = {};
  1325. seckillInfo.data = {};
  1326. ladderInfo.data = {};
  1327. }
  1328. firstLoading.value = false;
  1329. }
  1330. };
  1331. //视频播放结束时触发
  1332. const videoEnd = () => {
  1333. nextTick(() => {
  1334. proxy.refs.video.onended = () => {
  1335. currentDefaultImage.value = 0;
  1336. defaultImage.value = goodsDetail.data.defaultProduct.goodsPics[0];
  1337. };
  1338. });
  1339. };
  1340. // 促销活动信息
  1341. const getPreSell = async (productId) => {
  1342. let param = {
  1343. productId: productId,
  1344. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1345. };
  1346. const { data: value } = await useFetchRaw(
  1347. apiUrl + "v3/promotion/front/preSell/detail",
  1348. { params: param }
  1349. );
  1350. const res = value.value._rawValue;
  1351. if (res.state == 200) {
  1352. let now = new Date();
  1353. let preStartDate = new Date(res.data.startTime);
  1354. let preEndDate = new Date(res.data.endTime);
  1355. preSellInfo.data = res.data;
  1356. let countTime = 0;
  1357. preSellInfo.data.endTime = formatPreTime(new Date(res.data.endTime));
  1358. preSellInfo.data.startTime = formatPreTime(new Date(res.data.startTime));
  1359. if (now > preStartDate && now < preEndDate) {
  1360. preSellInfo.data.pre_run = 2; //活动进行中
  1361. countTime = res.data.distanceEndTime;
  1362. countDown(countTime);
  1363. } else if (now < preStartDate) {
  1364. preSellInfo.data.pre_run = 1; //活动未开始
  1365. countTime =
  1366. (new Date(res.data.startTime).getTime() - now.getTime()) / 1000;
  1367. countDown(countTime);
  1368. } else if (now > preEndDate) {
  1369. preSellInfo.data.pre_run = 3; //活动已结束
  1370. }
  1371. genQrcode();
  1372. } else {
  1373. ElMessage.error(res.msg);
  1374. }
  1375. };
  1376. const getPin = () => {
  1377. let param = {
  1378. productId: goodsDetail.data.defaultProduct.productId,
  1379. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1380. };
  1381. get("v3/promotion/front/spell/detail", param).then((res) => {
  1382. if (res.state == 200) {
  1383. pinInfo.data = res.data;
  1384. let countTime = 0;
  1385. let now = new Date();
  1386. let startTime = new Date(res.data.startTime);
  1387. if (now < startTime) {
  1388. countTime = (startTime.getTime() - now.getTime()) / 1000;
  1389. countDown(countTime);
  1390. } else {
  1391. countTime = res.data.distanceEndTime;
  1392. countDown(countTime);
  1393. }
  1394. genQrcode();
  1395. } else {
  1396. ElMessage.error(res.msg);
  1397. }
  1398. });
  1399. };
  1400. const getSeckill = () => {
  1401. let param = {
  1402. productId: goodsDetail.data.defaultProduct.productId,
  1403. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1404. };
  1405. get("v3/promotion/front/seckill/detail", param).then((res) => {
  1406. if (res.state == 200) {
  1407. seckillInfo.data = res.data;
  1408. let now = new Date();
  1409. let countTime = 0;
  1410. let startTime = new Date(res.data.startTime);
  1411. if (seckillInfo.data.state == 1 || seckillInfo.data.state == 2) {
  1412. countTime = res.data.distanceEndTime;
  1413. countDown(countTime);
  1414. } else {
  1415. countTime = startTime.getTime() - now.getTime();
  1416. }
  1417. genQrcode();
  1418. }
  1419. });
  1420. };
  1421. const countDown = (countTime) => {
  1422. secInt.value = setInterval(() => {
  1423. if (countTime == 0) {
  1424. getInitDataDynamic(calcProductId(route.path));
  1425. clearInterval(secInt.value);
  1426. } else {
  1427. countTime--;
  1428. let day = parseInt(countTime / 60 / 60 / 24);
  1429. let hours = parseInt((countTime / 60 / 60) % 24);
  1430. let minutes = parseInt((countTime / 60) % 60);
  1431. let seconds = parseInt(countTime % 60);
  1432. time.day = day;
  1433. time.hours = hours > 9 ? hours : "0" + hours;
  1434. time.minutes = minutes > 9 ? minutes : "0" + minutes;
  1435. time.seconds = seconds > 9 ? seconds : "0" + seconds;
  1436. }
  1437. }, 1000);
  1438. };
  1439. const formatPreTime = (time) => {
  1440. let op = new Date(time);
  1441. let year = op.getFullYear();
  1442. let month = op.getMonth() + 1;
  1443. let day = op.getDate();
  1444. let hour = op.getHours();
  1445. let minute = op.getMinutes();
  1446. let part1 = [year, month, day]
  1447. .map((i) => (i.toString().length < 2 ? `0${i}` : i))
  1448. .join("-");
  1449. let part2 = [hour, minute]
  1450. .map((i) => (i.toString().length < 2 ? `0${i}` : i))
  1451. .join(":");
  1452. return part1 + " " + part2;
  1453. };
  1454. const getLadder = () => {
  1455. let param = {
  1456. productId: goodsDetail.data.defaultProduct.productId,
  1457. promotionId: goodsDetail.data.defaultProduct.promotionId,
  1458. };
  1459. get("v3/promotion/front/ladder/group/detail", param).then((res) => {
  1460. if (res.state == 200) {
  1461. ladderInfo.data = res.data;
  1462. let now = new Date();
  1463. let countTime = 0;
  1464. let startTime = new Date(res.data.startTime);
  1465. if (now < startTime) {
  1466. countTime = (startTime.getTime() - now.getTime()) / 1000;
  1467. countDown(countTime);
  1468. ladderInfo.data.state = 1;
  1469. } else {
  1470. countTime = res.data.distanceEndTime;
  1471. countDown(countTime);
  1472. ladderInfo.data.state = 2;
  1473. }
  1474. genQrcode();
  1475. }
  1476. });
  1477. };
  1478. const genQrcode = () => {
  1479. if (judgeStock.value) {
  1480. return;
  1481. }
  1482. proxy.$nextTick(() => {
  1483. let canvas = qrcanvas({
  1484. data: goodsDetail.data.shareLink, //二维码内容
  1485. size: 125,
  1486. colorDark: "red",
  1487. });
  1488. setTimeout(() => {
  1489. document.getElementById("qrcodeAct").innerHTML = "";
  1490. document.getElementById("qrcodeAct").appendChild(canvas);
  1491. }, 10);
  1492. });
  1493. };
  1494. // 促销活动信息end
  1495. const recomOffShop = reactive({ data: [] });
  1496. const getRecom = () => {
  1497. get("v3/goods/front/goods/goodsList", {
  1498. storeId: goodsDetail.data.categoryId1,
  1499. }).then((res) => {
  1500. if (res.state == 200) {
  1501. let top = Math.floor(Math.random() * (res.data.list.length - 8)) + 8;
  1502. let end = top - 8;
  1503. recomOffShop.data = res.data.list
  1504. .filter(
  1505. (item) =>
  1506. item.defaultProductId != goodsDetail.data.defaultProduct.productId
  1507. )
  1508. .slice(end, top);
  1509. }
  1510. });
  1511. };
  1512. //添加足迹
  1513. const addLog = () => {
  1514. let params = {
  1515. productId: productId.value,
  1516. };
  1517. post("v3/member/front/productLookLog/add", params).then((res) => {});
  1518. };
  1519. //记录下载
  1520. const downloadAdd = (item) => {
  1521. let url = item.annexUrl;
  1522. let name = item.annexName;
  1523. if (filtersStore.getLoginFlag) {
  1524. post("v3/member/front/download/add", {
  1525. goodsId: item.goodsId,
  1526. annexId: item.annexId,
  1527. }).then((res) => {
  1528. });
  1529. }
  1530. const link = document.createElement("a");
  1531. fetch(url)
  1532. .then((res) => res.blob())
  1533. .then((blob) => {
  1534. link.href = URL.createObjectURL(blob);
  1535. link.download = name;
  1536. document.body.appendChild(link);
  1537. link.click();
  1538. window.URL.revokeObjectURL(link.href);
  1539. document.body.removeChild(link);
  1540. });
  1541. };
  1542. /**
  1543. * 选择规格值
  1544. * @param type:类型 值:choice,规格选择 default:默认
  1545. * @param specId:父级规格值
  1546. * @param specValueId:点击的当前的规格值
  1547. */
  1548. const selectSpecVal = (type, specId, specValueId) => {
  1549. isChoice.value = type == "choice" ? "choice" : "default";
  1550. let curParSpec = []; //当前点击的规格的父级id的当前项
  1551. curParSpec = goodsDetail.data.specs.filter((item) => item.specId == specId);
  1552. let curSPec = []; //当前点击的规格的规格id的当前项
  1553. curSPec = curParSpec[0].specValueList.filter(
  1554. (item1) => item1.specValueId == specValueId
  1555. );
  1556. curSPec[0].checkState = 1;
  1557. //被选择的规格值的id
  1558. let choiceSpecIds = [];
  1559. goodsDetail.data.specs.forEach((item) => {
  1560. if (item.specId != specId) {
  1561. item.specValueList.forEach((item1) => {
  1562. if (item1.checkState == "1") {
  1563. // checkState: 1-选中,2-可选,3-禁用
  1564. choiceSpecIds.push(item1.specValueId);
  1565. }
  1566. });
  1567. } else {
  1568. choiceSpecIds.push(specValueId);
  1569. }
  1570. });
  1571. let params = {
  1572. goodsId: goodsDetail.data.goodsId,
  1573. specValueIds: choiceSpecIds.join(","),
  1574. };
  1575. get("v3/goods/front/goods/productInfo", params).then((res) => {
  1576. if (res.state == 200) {
  1577. let result = res.data;
  1578. goodsDetail.data.defaultProduct = result.defaultProduct;
  1579. productId.value = result.defaultProduct.productId;
  1580. goodsDetail.data.specs = result.specs;
  1581. defaultImage.value = goodsDetail.data.defaultProduct.goodsPics[0];
  1582. currentDefaultImage.value = 0;
  1583. isShowQr.value = false;
  1584. // getInitDataDynamic(productId.value)
  1585. }
  1586. });
  1587. };
  1588. //改变数量按钮样式
  1589. const disStyle = reactive({
  1590. //目的是进入商品详情页面就让减按钮呈现禁止状态
  1591. color: "#DDDDDD",
  1592. backgroundColor: "#F8F8F8",
  1593. });
  1594. watch(
  1595. () => currentSpecNum.value,
  1596. () => {
  1597. //监听数量对加和减的样式做出调整
  1598. let productStock = goodsDetail.data.defaultProduct.productStock;
  1599. if (goodsDetail.data.defaultProduct.productStock == 0) {
  1600. productStock = 999;
  1601. }
  1602. if (currentSpecNum.value >= productStock) {
  1603. proxy.refs.add.style.color = "#DDDDDD";
  1604. proxy.refs.add.style.backgroundColor = "#F8F8F8";
  1605. } else if (currentSpecNum.value <= 1) {
  1606. disStyle.color = "#DDDDDD";
  1607. disStyle.backgroundColor = "#F8F8F8";
  1608. } else {
  1609. disStyle.color = "";
  1610. disStyle.backgroundColor = "";
  1611. proxy.refs.add.style.color = "";
  1612. proxy.refs.add.style.backgroundColor = "";
  1613. }
  1614. }
  1615. );
  1616. watch(currentSpecNum, () => {
  1617. if (currentSpecNum.value > goodsDetail.data.defaultProduct.productStock) {
  1618. currentSpecNum.value = goodsDetail.data.defaultProduct.productStock;
  1619. }
  1620. let reg = /\./g;
  1621. let reg0 = /0+\d/;
  1622. if (
  1623. currentSpecNum.value &&
  1624. (reg.test(currentSpecNum.value) || currentSpecNum.value <= 0)
  1625. ) {
  1626. currentSpecNum.value = 1;
  1627. }
  1628. });
  1629. //发送询盘
  1630. const goBuy = () => {
  1631. enquiryVis.value = true;
  1632. };
  1633. //关闭登录弹框
  1634. const closeEnquiryModal = () => {
  1635. enquiryVis.value = false;
  1636. };
  1637. //获取看了又看商品(人气数)
  1638. const getRecommend = async () => {
  1639. let params = {
  1640. storeId: vid.value,
  1641. sort: 5,
  1642. pageSize: 3,
  1643. current: 1,
  1644. };
  1645. const { data: value } = await useFetchRaw(
  1646. apiUrl + "v3/goods/front/goods/goodsList",
  1647. { params: params, key: params.sort.toString() }
  1648. );
  1649. const res = value._rawValue;
  1650. if (res.state == 200) {
  1651. let result = res.data;
  1652. recommendeList.data = result.list;
  1653. recommendeList.data.map(
  1654. (item) => (item.goodsPrice = new Number(item.goodsPrice).toFixed(2))
  1655. );
  1656. } else {
  1657. ElMessage.error(res.msg);
  1658. }
  1659. };
  1660. //获取店铺推荐的商品(销量数);获取热门收藏的商品(收藏数) type:recommend销量数 collection:收藏数
  1661. const getStorePopular = async (type) => {
  1662. if (type == "" || !type) {
  1663. type = "recommend";
  1664. } else {
  1665. storePopularType.value = type;
  1666. }
  1667. let params = {
  1668. storeId: vid.value,
  1669. sort:
  1670. storePopularType.value == "recommend"
  1671. ? 1
  1672. : storePopularType.value == "collection"
  1673. ? 6
  1674. : "",
  1675. pageSize: 6,
  1676. current: 1,
  1677. };
  1678. const { data: value } = await useFetchRaw(
  1679. apiUrl + "v3/goods/front/goods/goodsList",
  1680. { params: params, key: params.sort.toString() }
  1681. );
  1682. const res = value._rawValue;
  1683. if (res.state == 200) {
  1684. let result = res.data;
  1685. storePopularList.data = result.list;
  1686. storePopularList.data.map(
  1687. (item) => (item.goodsPrice = new Number(item.goodsPrice).toFixed(2))
  1688. );
  1689. } else {
  1690. ElMessage.error(res.msg);
  1691. }
  1692. };
  1693. //切换商品详情,评价,服务,热销
  1694. const goodsDescType = (type) => {
  1695. // let elementScrollTop =
  1696. // window.pageYOffset ||
  1697. // document.documentElement.scrollTop ||
  1698. // document.body.scrollTop; //获取页面滚动高度
  1699. // if (elementScrollTop > containerTop.value + 33 + 48) {
  1700. // window.scrollTo({
  1701. // top: containerTop.value,
  1702. // behavior: "smooth",
  1703. // });
  1704. // }
  1705. if (type == "" || !type) {
  1706. goodsDesctionType.value = "detail";
  1707. } else {
  1708. goodsDesctionType.value = type;
  1709. }
  1710. if (type == "salestore") {
  1711. getSalestore();
  1712. }
  1713. };
  1714. //获取商品评价
  1715. const getEvaluation = (type) => {
  1716. if (!type) {
  1717. evaluationType.value = "";
  1718. } else {
  1719. evaluationType.value = type;
  1720. }
  1721. let params = {
  1722. productId: productId.value,
  1723. current: evaluationCurrent.value,
  1724. pageSize: evaluationPageSize.value,
  1725. type: evaluationType.value,
  1726. };
  1727. get("v3/goods/front/goods/comment", params).then((res) => {
  1728. if (res.state == 200) {
  1729. let result = res.data;
  1730. goodsCommentsInfo.data = result;
  1731. score.value = Number(goodsCommentsInfo.data.avgScore);
  1732. goodsCommentsInfo.data &&
  1733. goodsCommentsInfo.data.list &&
  1734. goodsCommentsInfo.data.list.length > 0 &&
  1735. goodsCommentsInfo.data.list.map((commentsItem) => {
  1736. commentsItem.memberName =
  1737. commentsItem.memberName.slice(0, 1) +
  1738. "***" +
  1739. commentsItem.memberName.slice(
  1740. commentsItem.memberName.length - 1,
  1741. commentsItem.memberName.length
  1742. );
  1743. });
  1744. } else {
  1745. ElMessage.error(res.msg);
  1746. }
  1747. });
  1748. };
  1749. //评价列表上一页
  1750. const handlePrevCilickChange = () => {
  1751. if (evaluationCurrent.value == 1) {
  1752. evaluationCurrent.value = 1;
  1753. } else {
  1754. evaluationCurrent.value--;
  1755. }
  1756. getEvaluation(evaluationType.value);
  1757. };
  1758. //评价列表下一页
  1759. const handleNextCilickChange = () => {
  1760. if (evaluationCurrent.value >= goodsCommentsInfo.data.list.length) {
  1761. evaluationCurrent.value = goodsCommentsInfo.data.list.length;
  1762. } else {
  1763. evaluationCurrent.value++;
  1764. }
  1765. getEvaluation(evaluationType.value);
  1766. };
  1767. //评价列表当前选择页
  1768. const handleCurrentChange = (val) => {
  1769. evaluationCurrent.value = val;
  1770. getEvaluation(evaluationType.value);
  1771. };
  1772. //获取热销店铺推荐
  1773. const getSalestore = () => {
  1774. let params = {
  1775. storeId: goodsDetail.data.storeInf.storeId,
  1776. sort: 7,
  1777. pageSize: salesPageSize.value,
  1778. current: salesCurrent.value,
  1779. };
  1780. get("v3/goods/front/goods/goodsList", params).then((res) => {
  1781. if (res.state == 200) {
  1782. let result = res.data;
  1783. recommendeData.data = result;
  1784. recommendedList.data = result.list;
  1785. recommendedList.data.map(
  1786. (item) => (item.goodsPrice = new Number(item.goodsPrice).toFixed(2))
  1787. );
  1788. } else {
  1789. ElMessage.error(res.msg);
  1790. }
  1791. });
  1792. };
  1793. //店铺热销列表上一页
  1794. const handlePrevCilickChangeSales = () => {
  1795. if (evaluationCurrent.value == 1) {
  1796. salesCurrent.value = 1;
  1797. } else {
  1798. salesCurrent.value--;
  1799. }
  1800. getSalestore();
  1801. };
  1802. //店铺热销列表下一页
  1803. const handleNextCilickChangeSales = () => {
  1804. if (salesCurrent.value >= goodsCommentsInfo.data.list.length) {
  1805. salesCurrent.value = goodsCommentsInfo.data.list.length;
  1806. } else {
  1807. salesCurrent.value++;
  1808. }
  1809. getSalestore();
  1810. };
  1811. //店铺热销列表当前选择页
  1812. const handleCurrentChangeSales = (val) => {
  1813. salesCurrent.value = val;
  1814. getSalestore();
  1815. };
  1816. //关注店铺及取消关注
  1817. const headercat = ref(null)
  1818. const focusStore = () => {
  1819. if (filtersStore.getLoginFlag) {
  1820. //已登录
  1821. let params = {
  1822. storeIds: goodsDetail.data.storeInf.storeId,
  1823. isCollect: !goodsDetail.data.storeInf.isFollowStore,
  1824. };
  1825. post("v3/member/front/followStore/edit", params).then((res) => {
  1826. if (res.state == 200) {
  1827. goodsDetail.data.storeInf.isFollowStore =!goodsDetail.data.storeInf.isFollowStore;
  1828. proxy.refs.headercat.setfollowStore(goodsDetail.data.storeInf.isFollowStore? "true" : "false")
  1829. if (goodsDetail.data.storeInf.isFollowStore) {
  1830. sldStatEvent({
  1831. behaviorType: "fol",
  1832. storeId: goodsDetail.data.storeInf.storeId,
  1833. });
  1834. }
  1835. }
  1836. });
  1837. } else {
  1838. //未登录提示登录
  1839. return openLoginDialog({
  1840. onRegister: () => {
  1841. router.push({
  1842. path: "/register",
  1843. });
  1844. },
  1845. onForgot: () => {
  1846. router.push({
  1847. path: "/member/login/forget",
  1848. });
  1849. },
  1850. });
  1851. }
  1852. };
  1853. //商品收藏及取消收藏
  1854. const collectGoods = () => {
  1855. if (filtersStore.getLoginFlag) {
  1856. //已登录
  1857. let params = {
  1858. productIds: productId.value,
  1859. isCollect: !goodsDetail.data.followGoods,
  1860. };
  1861. post("v3/member/front/followProduct/edit", params).then((res) => {
  1862. if (res.state == 200) {
  1863. goodsDetail.data.followGoods = !goodsDetail.data.followGoods;
  1864. if (goodsDetail.data.followGoods) {
  1865. sldStatEvent({
  1866. behaviorType: "fav",
  1867. goodsId: goodsDetail.data.goodsId,
  1868. storeId: goodsDetail.data.storeInf.storeId,
  1869. });
  1870. }
  1871. } else {
  1872. ElMessage.error(res.msg);
  1873. }
  1874. });
  1875. } else {
  1876. //未登录提示登录
  1877. return openLoginDialog({
  1878. onRegister: () => {
  1879. router.push({
  1880. path: "/register",
  1881. });
  1882. },
  1883. onForgot: () => {
  1884. router.push({
  1885. path: "/member/login/forget",
  1886. });
  1887. },
  1888. });
  1889. }
  1890. };
  1891. //点击查看全部查看全部的商品规格参数
  1892. const openParameter = () => {
  1893. parameterShow.value = !parameterShow.value;
  1894. };
  1895. //选择商品主图
  1896. const selectDefaultImage = (goodsImgItem, goodsImgIndex) => {
  1897. defaultImage.value = goodsImgItem;
  1898. currentDefaultImage.value = goodsImgIndex;
  1899. };
  1900. //切换商品主图
  1901. const switchDefaultImage = (type) => {
  1902. let defaultImagelength = goodsDetail.data.defaultProduct.goodsPics.length;
  1903. if (type == "left") {
  1904. currentDefaultImage.value--;
  1905. if (currentDefaultImage.value <= 0) {
  1906. currentDefaultImage.value = 0;
  1907. }
  1908. defaultImage.value =
  1909. goodsDetail.data.defaultProduct.goodsPics[currentDefaultImage.value];
  1910. } else {
  1911. currentDefaultImage.value++;
  1912. if (currentDefaultImage.value >= defaultImagelength) {
  1913. currentDefaultImage.value = defaultImagelength - 1;
  1914. }
  1915. defaultImage.value =
  1916. goodsDetail.data.defaultProduct.goodsPics[currentDefaultImage.value];
  1917. }
  1918. goodsPictureListsLeft();
  1919. };
  1920. //图片列表的left移动的距离
  1921. const goodsPictureListsLeft = () => {
  1922. //获取 goods_picture_list 的元素
  1923. let goodsPictureLists = goodsPictureList.value;
  1924. //列表默认显示5张图片
  1925. if (
  1926. goodsDetail.data.defaultProduct.goodsPics.length > 5 &&
  1927. currentDefaultImage.value >= 0
  1928. ) {
  1929. /* 分析找规律:
  1930. 如果有8张图片,点击右键 最大可以向左移动的距离为 8 - 5既3张图,left移动的距离为 3 * - 66px; currentDefaultImage.value从0开始的
  1931. 其中:66px为每一个元素需要每次移动的距离 为图片的宽度与图片之间的间距的和
  1932. currentDefaultImage.value == 4;goodsPictureLists.style.left = 0;
  1933. currentDefaultImage.value == 5;goodsPictureLists.style.left = (5-4) (1) * -66px;
  1934. currentDefaultImage.value == 6;goodsPictureLists.style.left = (5-2) (2) * -66px;
  1935. currentDefaultImage.value == 7;goodsPictureLists.style.left = (5-3) (2) * -66px;
  1936. 点击左键,最大可以向右移动的距离为 8 - 5 即3张
  1937. currentDefaultImage.value == 0;goodsPictureLists.style.left = 0;
  1938. currentDefaultImage.value == 1; goodsPictureLists.style.left = -66px;
  1939. currentDefaultImage.value == 2; goodsPictureLists.style.left = 2 * -66px;
  1940. currentDefaultImage.value == 3; goodsPictureLists.style.left = 3 * -66px;
  1941. */
  1942. if (currentDefaultImage.value > 4) {
  1943. goodsPictureLists.style.left =
  1944. (currentDefaultImage.value - 4) * -66 + "px";
  1945. }
  1946. if (
  1947. currentDefaultImage.value <
  1948. goodsDetail.data.defaultProduct.goodsPics.length - 4
  1949. ) {
  1950. goodsPictureLists.style.left = currentDefaultImage.value * -66 + "px";
  1951. }
  1952. }
  1953. };
  1954. //获取元素距离父元素的顶部及左边的距离
  1955. const offset = (el) => {
  1956. if (process.client) {
  1957. let top = el.offsetTop;
  1958. let left = el.offsetLeft;
  1959. if (el.offsetParent) {
  1960. el = el.offsetParent;
  1961. top += el.offsetTop;
  1962. left += el.offsetLeft;
  1963. }
  1964. return {
  1965. left: left,
  1966. top: top,
  1967. };
  1968. }
  1969. };
  1970. if (process.client) {
  1971. nextTick(() => {
  1972. containerTop.value = offset(container.value).top;
  1973. window.addEventListener("scroll", scrollHandle); //绑定页面滚动事件
  1974. window.addEventListener("click", () => {
  1975. addrDialogVisible.value = false;
  1976. });
  1977. });
  1978. }
  1979. //鼠标移动
  1980. const move = (e) => {
  1981. if (currentDefaultImage.value == -1) {
  1982. return;
  1983. }
  1984. //主图父元素的信息 宽,高
  1985. let previewsBox = previewBox.value;
  1986. let previewBoxWidth = previewsBox.offsetWidth;
  1987. let previewBoxHeight = previewsBox.offsetHeight;
  1988. //主图父元素距离顶部的距离
  1989. let previewsBoxLeft = offset(previewsBox).left;
  1990. let previewsBoxTop = offset(previewsBox).top;
  1991. // 遮罩盒子的信息宽,高
  1992. let masksBox = maskBox.value;
  1993. let maskBoxWidth = masksBox.offsetWidth;
  1994. let maskBoxHeight = masksBox.offsetHeight;
  1995. //鼠标距离屏幕距离
  1996. let moveX = e.clientX;
  1997. let moveY = e.clientY;
  1998. //获取左侧大图父元素的信息
  1999. let zoomsBox = zoomBox.value;
  2000. let zoomBoxWidth = zoomsBox.offsetWidth;
  2001. let zoomBoxHeight = zoomsBox.offsetHeight;
  2002. // 获取大图元素的信息宽,高
  2003. let pictureBigBox = pictureBig.value;
  2004. let pictureBigWidth = pictureBigBox.offsetWidth;
  2005. let pictureBigHeight = pictureBigBox.offsetHeight;
  2006. //获取滚动条的高度
  2007. let scroll = document.documentElement.scrollTop || document.body.scrollTop;
  2008. //主图距离父元素的left及top值
  2009. let left = moveX - previewsBoxLeft - maskBoxWidth / 2;
  2010. let top;
  2011. if (scroll > 0) {
  2012. top = moveY - previewsBoxTop + scroll - maskBoxHeight / 2;
  2013. } else {
  2014. top = moveY - previewsBoxTop - maskBoxHeight / 2;
  2015. }
  2016. //移动限制最大宽度,及最大高度
  2017. let maxWidth = previewBoxWidth - maskBoxWidth;
  2018. let maxHeight = previewBoxHeight - maskBoxHeight;
  2019. left = left < 0 ? 0 : left > maxWidth ? maxWidth : left;
  2020. top = top < 0 ? 0 : top > maxHeight ? maxHeight : top;
  2021. //比列
  2022. let parcentX = left / maxWidth;
  2023. let parcentY = top / maxHeight;
  2024. //遮罩层的定位值
  2025. maskBox.value.style.left = left + "px";
  2026. maskBox.value.style.top = top + "px";
  2027. //大图元素的定位值
  2028. pictureBig.value.style.left =
  2029. parcentX * (zoomBoxWidth - pictureBigWidth) + "px";
  2030. pictureBig.value.style.top =
  2031. parcentY * (zoomBoxHeight - pictureBigHeight) + "px";
  2032. pictureBig.value.style.width =
  2033. (previewBoxWidth / maskBoxWidth) * zoomBoxWidth + "px";
  2034. pictureBig.value.style.height =
  2035. (previewBoxHeight / maskBoxHeight) * zoomBoxHeight + "px";
  2036. };
  2037. //鼠标移出
  2038. const out = () => {
  2039. maskShow.value = false;
  2040. };
  2041. //鼠标移入
  2042. const enter = () => {
  2043. if (currentDefaultImage.value == -1) {
  2044. return;
  2045. }
  2046. addrDialogVisible.value = false;
  2047. maskShow.value = true;
  2048. };
  2049. // 评论区查看图片
  2050. const showImg = (index, img) => {
  2051. imgSource.value = img;
  2052. imgIndex.value = index;
  2053. imgVisible.value = true;
  2054. };
  2055. //分享
  2056. const share = (type) => {
  2057. let title = goodsDetail.data.goodsName; //需要分享的标题,这里取商品名字
  2058. let url = goodsDetail.data.shareLink; //分享的地址,用户点击可以进入到该商品
  2059. let content = goodsDetail.data.goodsBrief; //自定义内容,这里取商品广告词
  2060. let targetUrl = ""; //跳转的url地址
  2061. if (type == "weixin") {
  2062. wxShareCode.value = true;
  2063. //微信
  2064. let canvas = qrcanvas({
  2065. data: url, //二维码内容
  2066. size: 100,
  2067. colorDark: "red",
  2068. });
  2069. let share_wx_qrcode = document.getElementById("share_wx_qrcode");
  2070. if (
  2071. wxShareCode.value &&
  2072. share_wx_qrcode != null &&
  2073. share_wx_qrcode != undefined
  2074. ) {
  2075. document.getElementById("share_wx_qrcode").innerHTML = "";
  2076. document.getElementById("share_wx_qrcode").appendChild(canvas);
  2077. }
  2078. } else if (type == "qzone") {
  2079. wxShareCode.value = false;
  2080. //QQ空间
  2081. targetUrl =
  2082. "https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?title=" +
  2083. encodeURIComponent(title) +
  2084. "&desc=" +
  2085. encodeURIComponent(content) +
  2086. "&summary=" +
  2087. encodeURIComponent(content) +
  2088. "&url=" +
  2089. encodeURIComponent(url);
  2090. window.open(targetUrl, "_blank");
  2091. } else if (type == "sina") {
  2092. wxShareCode.value = false;
  2093. //新浪微博
  2094. targetUrl =
  2095. "https://service.weibo.com/share/share.php?title=" +
  2096. encodeURIComponent(content + "「" + title + "」" + " 点这里" + url);
  2097. window.open(targetUrl, "_blank");
  2098. }
  2099. };
  2100. const refreshInfo = () => {
  2101. history.go(0);
  2102. };
  2103. const updateFllow = (e) => {
  2104. goodsDetail.data.storeInf.isFollowStore = e.state == "true" ? true : false;
  2105. };
  2106. //暴露的变量及方法
  2107. </script>
  2108. <style lang="scss">
  2109. @import "@/assets/style/base.scss";
  2110. @import "@/assets/style/theme.scss";
  2111. @import "@/assets/style/iconfont.css";
  2112. @import "@/assets/style/goodsDetail.scss";
  2113. .popular_list_empty {
  2114. height: 95px;
  2115. font-size: 14px;
  2116. /*font-family: Microsoft YaHei;*/
  2117. font-weight: 400;
  2118. color: #666666;
  2119. }
  2120. .imageBorder {
  2121. border: 1px solid #eee;
  2122. }
  2123. .goods_picture_big {
  2124. border: 1px solid #eee;
  2125. }
  2126. .el-radio__inner:hover {
  2127. border-color: $colorMain;
  2128. }
  2129. .el-radio__input.is-checked .el-radio__inner {
  2130. border-color: $colorMain;
  2131. background: $colorMain;
  2132. }
  2133. .el-radio__input.is-checked + .el-radio__label {
  2134. color: $colorMain;
  2135. }
  2136. .el-radio {
  2137. margin-bottom: 10px;
  2138. display: flex;
  2139. align-items: flex-start;
  2140. white-space: unset;
  2141. margin-right: unset;
  2142. }
  2143. .el-radio-button__inner,
  2144. .el-radio-group {
  2145. /* display: block; */
  2146. line-height: 1;
  2147. vertical-align: middle;
  2148. }
  2149. .el-radio__label {
  2150. font-size: 13px;
  2151. width: 320px;
  2152. overflow: hidden;
  2153. text-overflow: ellipsis;
  2154. display: -webkit-box;
  2155. -webkit-line-clamp: 2;
  2156. -webkit-box-orient: vertical;
  2157. word-break: break-all;
  2158. line-height: 22px;
  2159. margin-top: -5px;
  2160. }
  2161. .evaluationes {
  2162. color: #3b4 !important;
  2163. }
  2164. .sld_goods_detail .goods_htmls .ql-video {
  2165. width: 525px;
  2166. height: 315px;
  2167. }
  2168. .sld_goods_detail .goods_htmls a {
  2169. display: inline-block;
  2170. margin: 5px auto;
  2171. color: #0000ff;
  2172. text-decoration: underline;
  2173. }
  2174. .sld_goods_detail .goods_htmls table {
  2175. border-collapse: collapse;
  2176. padding: 0;
  2177. }
  2178. .sld_goods_detail .goods_htmls td,
  2179. .sld_goods_detail .goods_htmls th {
  2180. border: 1px solid #ddd;
  2181. padding: 5px 10px;
  2182. }
  2183. .sld_goods_detail .goods_htmls ol li,
  2184. .sld_goods_detail .goods_htmls ul li {
  2185. list-style: unset;
  2186. }
  2187. .sld_goods_detail {
  2188. .summary {
  2189. .coupon {
  2190. .el-dialog__header {
  2191. padding-top: 18px;
  2192. padding-bottom: 18px;
  2193. .el-dialog__title {
  2194. color: #333333;
  2195. /*font-family: Microsoft YaHei;*/
  2196. font-weight: bold;
  2197. }
  2198. .el-dialog__close {
  2199. color: #333333;
  2200. font-size: 20px;
  2201. }
  2202. }
  2203. .el-dialog__body {
  2204. background: #f8f8f8;
  2205. }
  2206. }
  2207. }
  2208. }
  2209. .summary-info{
  2210. }
  2211. .mt-20{
  2212. margin-top: 20px;
  2213. }
  2214. .summary_htmls img{
  2215. width: 100%;
  2216. }
  2217. </style>